using System.Diagnostics;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.nioneo.store
{


///
/// <summary> * Implementation of the node store. </summary>
/// 
	public class NodeStore : AbstractStore, Store
	{
	// node store version, each node store should end with this string
	// (byte encoded)
		private const string VERSION = "NodeStore v0.9.5";

	// in_use(byte)+next_rel_id(int)+next_prop_id(int)
		private const int RECORD_SIZE = 9;

///    
///     <summary> * See <seealso cref="AbstractStore#AbstractStore(String, Map)"/> </summary>
///     
		public NodeStore<T1>(string fileName, Map<T1> config) : base(fileName, config)
		{
		}

///    
///     <summary> * See <seealso cref="AbstractStore#AbstractStore(String)"/> </summary>
///     
		public NodeStore(string fileName) : base(fileName)
		{
		}

		public virtual string getTypeAndVersionDescriptor()
		{
			return VERSION;
		}

		public override int getRecordSize()
		{
			return RECORD_SIZE;
		}

///    
///     <summary> * Creates a new node store contained in <CODE>fileName</CODE> If filename
///     * is <CODE>null</CODE> or the file already exists an 
///     * <CODE>IOException</CODE> is thrown.
///     *  </summary>
///     * <param name="fileName">
///     *            File name of the new node store </param>
///     * <exception cref="IOException">
///     *             If unable to create node store or name null </exception>
///     
		public static void createStore(string fileName)
		{
			createEmptyStore(fileName, VERSION);
			NodeStore store = new NodeStore(fileName);
			NodeRecord nodeRecord = new NodeRecord(store.nextId());
			nodeRecord.setInUse(true);
			store.updateRecord(nodeRecord);
			store.Close();
		}

		public virtual NodeRecord getRecord(int id)
		{
			PersistenceWindow window = acquireWindow(id, OperationType.READ);
			try
			{
				NodeRecord record = getRecord(id, window, false);
				return record;
			}
			finally
			{
				releaseWindow(window);
			}
		}

		public virtual void updateRecord(NodeRecord record, bool recovered)
		{
			Debug.Assert(recovered);
			setRecovered();
			try
			{
				updateRecord(record);
			}
			finally
			{
				unsetRecovered();
			}
		}

		public virtual void updateRecord(NodeRecord record)
		{
			PersistenceWindow window = acquireWindow(record.getId(), OperationType.WRITE);
			try
			{
				updateRecord(record, window);
			}
			finally
			{
				releaseWindow(window);
			}
		}

		public virtual bool loadLightNode(int id)
		{
			PersistenceWindow window = null;
			try
			{
				window = acquireWindow(id, OperationType.READ);
			}
			catch (InvalidRecordException e)
			{
			// ok id to high
				return false;
			}

			try
			{
				NodeRecord record = getRecord(id, window, true);
				if (record == null)
				{
					return false;
				}
				return true;
			}
			finally
			{
				releaseWindow(window);
			}
		}

		private NodeRecord getRecord(int id, PersistenceWindow window, bool check)
		{
			Buffer buffer = window.getOffsettedBuffer(id);
			bool inUse = (buffer.get() == Record.IN_USE.byteValue());
			if (!inUse)
			{
				if (check)
				{
					return null;
				}
				throw new InvalidRecordException("Record[" + id + "] not in use");
			}
			NodeRecord nodeRecord = new NodeRecord(id);
			nodeRecord.setInUse(inUse);
			nodeRecord.setNextRel(buffer.getInt());
			nodeRecord.setNextProp(buffer.getInt());
			return nodeRecord;
		}

		private void updateRecord(NodeRecord record, PersistenceWindow window)
		{
			int id = record.getId();
			Buffer buffer = window.getOffsettedBuffer(id);
			if (record.inUse())
			{
				buffer.put(Record.IN_USE.byteValue()).putInt(record.getNextRel()).putInt(record.getNextProp());
			}
			else
			{
				buffer.put(Record.NOT_IN_USE.byteValue());
				if (!isInRecoveryMode())
				{
					freeId(id);
				}
			}
		}

		public override string ToString()
		{
			return "NodeStore";
		}

		protected internal override bool versionFound(string version)
		{
			if (!version.StartsWith("NodeStore"))
			{
			// non clean shutdown, need to do recover with right neo
				return false;
			}
			if (version.Equals("NodeStore v0.9.3"))
			{
				rebuildIdGenerator();
				closeIdGenerator();
				return true;
			}
			throw new IllegalStoreVersionException("Store version [" + version + "]. Please make sure you are not running old Neo4j kernel " + " towards a store that has been created by newer version " + " of Neo4j.");
		}
	}
}